#include "pjsipwrapper.h"

#include <pjlib.h>

#include <stdio.h>
/*****************************************************************************
 * A simple module to handle otherwise stateless  request. We will register
 * this with the application priority.
 */

PjApiUa *PjApiUa::m_ua = 0;

//static pj_status_t mod_handler_load(pjsip_endpoint *endpt);

/* The module instance. */
pjsip_module PjApiUa::mod_default_handler =
{
    NULL, NULL,				/* prev, next.		*/
    { "mod-default-handler", 19 },	/* Name.		*/
    -1,					/* Id			*/
    //PJSIP_MOD_PRIORITY_APPLICATION+1,	/* Priority	        */
    PJSIP_MOD_PRIORITY_TRANSPORT_LAYER,
    &(PjApiUa::mod_handler_load),				/* load()		*/
    NULL,				/* start()		*/
    NULL,				/* stop()		*/
    NULL,				/* unload()		*/
    &(PjApiUa::onStatelessRequest),		/* on_rx_request()	*/
    NULL,				/* on_rx_response()	*/
    NULL,				/* on_tx_request.	*/
    NULL,				/* on_tx_response()	*/
    NULL,				/* on_tsx_state()	*/

};

pj_status_t PjApiUa::mod_handler_load(pjsip_endpoint *endpt)
{
    //pj_str_t allowed[] = { "INFO2", 99};
    //pj_str_t accepted = { "text/plain", 19 };

    /* Register supported methods: INVITE, ACK, BYE, CANCEL, UPDATE */
    //pjsip_endpt_add_capability(endpt, &mod_default_handler, PJSIP_H_ALLOW, NULL,
	//		       PJ_ARRAY_SIZE(allowed), allowed);

    /* Register "application/sdp" in Accept header */
    //pjsip_endpt_add_capability(endpt, &mod_default_handler, PJSIP_H_ACCEPT, NULL,
	//		       1, &accepted);

    return PJ_SUCCESS;
}




/*
pjsua_config PjApiUa::m_uaConfig;
pjsua_logging_config PjApiUa::m_logConfig;
pjsua_media_config PjApiUa::m_mediaConfig;
pjsua_acc_config PjApiUa::m_accountConfig;
pjsua_transport_config PjApiUa::m_transportConfig;
*/
PjApiUa::PjApiUa()
{
    //init();


}

PjApiUa::~PjApiUa()
{

}

PjApiUa* PjApiUa::getInstance()
{
    if(m_ua == NULL)
    {
        m_ua = new PjApiUa();
    }
    return m_ua;
}
void PjApiUa::init()
{
    pj_status_t status;

    status = pjsua_create();

    if (status != PJ_SUCCESS)
    {
        //pjsua_perror(THIS_FILE, "Error initializing pjsua", status);
        return;
    }

    // Initialize configs with default settings.
    pjsua_config_default(&m_uaConfig);
    pjsua_logging_config_default(&m_logConfig);
    pjsua_media_config_default(&m_mediaConfig);


    setCallback();

    //setUaConfig();
    // Initialize pjsua
    //printf("init UA\n");
    status = pjsua_init(&m_uaConfig, &m_logConfig, &m_mediaConfig);
    if (status != PJ_SUCCESS) {
          //pjsua_perror(THIS_FILE, "Error initializing pjsua", status);
          return;
    }
        /* Initialize our module to handle otherwise unhandled request */
    //mod_default_handler.default_mod_on_rx_request =
   // status = pjsip_endpt_register_module(pjsua_get_pjsip_endpt(),
	//				 &mod_default_handler);
    //mod_handler_load(pjsua_get_pjsip_endpt());
   // if (status != PJ_SUCCESS)
   // printf("ERROR: register module");
	//return ;

}

void PjApiUa::start()
{
    pj_status_t status;

    // Start pjsua
    status = pjsua_start();
    if (status != PJ_SUCCESS) {
        pjsua_destroy();
        ///pjsua_perror(THIS_FILE, "Error starting pjsua", status);
        return;
    }

}

void PjApiUa::terminate()
{
    if(m_ua)
    {
        PJ_LOG(3, ("PjApiUa", "UA will be deleted"));
        //delete m_ua;
    }
    pjsua_destroy();
    PJ_LOG(3, ("PjApiUa", "terminate"));
}

void PjApiUa::registerAccountHandler(PjApiUaAccountHandler *handler)
{
    m_accountHandler = handler;
}

PjApiUaAccount * PjApiUa::getUaAccount() const
{
    if(!m_accountHandler)
    {
        return NULL;
    }
    return m_accountHandler->getAccount();
}

PjApiUaAccountHandler* PjApiUa::getUaAccountHandler() const
{
    return m_accountHandler;
}

void PjApiUa::setAccountConfig(pj_str_t id,
                            pj_str_t reg,
                            pj_str_t proxy,
                            pj_str_t username,
                            pj_str_t password,
                            pj_str_t realm)
{

    pj_status_t status;
    pjsua_acc_id id__;

    PJ_LOG(3, ("PjApiUa","start add account"));
    pjsua_acc_config_default(&m_accountConfig);
    pj_strassign(&m_accountConfig.id, &id);
    pj_strassign(&m_accountConfig.reg_uri, &reg);
    m_accountConfig.proxy_cnt = 1;
    m_accountConfig.cred_count = 1;
    pj_strassign(&m_accountConfig.proxy[0], &proxy);
    pj_strassign(&m_accountConfig.cred_info[0].username, &username);
    pj_strassign(&m_accountConfig.cred_info[0].data, &password);
    pj_strassign(&m_accountConfig.cred_info[0].realm, &realm);

    status = pjsua_acc_add(&m_accountConfig, PJ_TRUE, &id__);
    if ((status != PJ_SUCCESS) &&(m_accountHandler))
    {
        m_accountHandler->notifyServerRegistry(false);
        return;
    }
    PJ_LOG(3, ("PjApiUa","end add account"));

    if(m_accountHandler)
    {
        m_accountHandler->notifyServerRegistry(true);

        m_accountHandler->getAccount()->setAccountID(id__);
    }

    PJ_LOG(3, ("PjApiUa","Complete add account"));

}

void PjApiUa::setLogConfig(pjsua_logging_config *config)
{
}

void PjApiUa::setMediaConfig(pjsua_media_config *config)
{
}

void PjApiUa::setNullDevice()
{
    pj_status_t status;

    status = pjsua_set_null_snd_dev();
    if(status != PJ_SUCCESS)
    {
        PJ_LOG(3,("PjUa"," can not set null device"));
    }

}

void PjApiUa::setUaConfig(pjsua_config *config)
{

}

void PjApiUa::setTransportConfig(pjsip_transport_type_e type, int port)
{
    pjsua_transport_id *id;
    pj_status_t status;
    pjsua_transport_config_default(&m_transportConfig);
    m_transportConfig.port = port;
    status = pjsua_transport_create(type, &m_transportConfig, id);
    if(status != PJ_SUCCESS) return;
    m_accountHandler->setTransportID(*id);
}

void PjApiUa::setMediaTransportConfig(int port)
{
    pjsua_transport_config_default(&m_mediaTransportConfig);
    m_mediaTransportConfig.port = port;
    if(m_mediaTransportConfig.port <1)
    {
        m_mediaTransportConfig.port = 4000;
    }

    pj_status_t status;
#ifdef TRANSPORT_ADAPTER_SAMPLE
    status = transport_adapter_sample();
#else
    status = pjsua_media_transports_create(&m_mediaTransportConfig);
#endif

    if(status != PJ_SUCCESS)
    {
        PJ_LOG(3, ("ua", "set media transport falied"));
        return;
    }
    PJ_LOG(3, ("ua", "set media transport OK"));



}
void PjApiUa::onCallState(pjsua_call_id call_id, pjsip_event *e)
{
    PJ_LOG(3, ("On call state", "call state changed"));
}

void PjApiUa::onCallTsxState(pjsua_call_id call_id, pjsip_transaction *tsx, pjsip_event *e)
{
    //Here we only handler INFO message
    // expand if nesssary

    if(PjApiUa::getInstance()->getUaAccountHandler() == NULL)
    {
        PJ_LOG(3,("","no handler"));
        return;
    }

    if(tsx == NULL)
    {
        return;
    }

// to change
    const pjsip_method info_method =
    {
	PJSIP_OTHER_METHOD,
	{ "INFO", 4 }
    };

    if (pjsip_method_cmp(&tsx->method, &info_method)==0)
    {
        /*
         * Handle INFO method.
         */
        if (tsx->role == PJSIP_ROLE_UAC &&
            (tsx->state == PJSIP_TSX_STATE_COMPLETED ||
               (tsx->state == PJSIP_TSX_STATE_TERMINATED &&
                e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED)))
        {
            /* Status of outgoing INFO request */
            if (tsx->status_code >= 200 && tsx->status_code < 300)
            {
                PJ_LOG(4,("ua",
                      "Call %d: REQUEST sent successfully with INFO",
                      call_id));
                pjsip_rx_data *rdata;

                rdata = e->body.tsx_state.src.rdata;
                PJ_LOG(3,("ua", "Call %d: response with INFO:\n%.*s",
                                  call_id,
                                  (int)rdata->msg_info.msg->body->len,
                                  rdata->msg_info.msg->body->data));

                //Notify the upper layer
                if((!PjApiUa::getInstance()->getUaAccountHandler())||(!PjApiUa::getInstance()->getUaAccountHandler()->getAccountCallback()))
                {
                    PJ_LOG(4, ("hander or callback is not set",""));
                    return;
                }
                /*
                PjApiUa::getInstance()->getUaAccountHandler()->getAccountCallback()->onInfoResponse(call_id,
                                (std::string)(rdata->msg_info.msg->hdr.name.ptr),
                                (std::string)(rdata->msg_info.msg->body->content_type.type.ptr),
                                rdata->msg_info.msg->body->data);
                */

            }
            else if (tsx->status_code >= 300)
            {
                PJ_LOG(4,("ua",
                      "Call %d: Failed to send REQUEST with INFO: %d/%.*s",
                      call_id,
                      tsx->status_code,
                      (int)tsx->status_text.slen,
                      tsx->status_text.ptr));
            }
        }
        else if (tsx->role == PJSIP_ROLE_UAS &&
               tsx->state == PJSIP_TSX_STATE_TRYING)
        {
            /* Answer incoming INFO with 200/OK */
            pjsip_rx_data *rdata;
            pjsip_tx_data *tdata;
            pj_status_t status;

            rdata = e->body.tsx_state.src.rdata;

            if (rdata->msg_info.msg->body) {

                PJ_LOG(3,("ua", "Call %d: incoming INFO:\n%.*s",
                      call_id,
                      (int)rdata->msg_info.msg->body->len,
                      rdata->msg_info.msg->body->data));

                 //Notify the upper layer, the incoming request
                /**
                 * Define Info request format
                 *  request name: -> rdata->msg_info.msg->hdr->name
                 *  request content type ->data->msg_info.msg->body->content_type.type
                 *  request body: -> rdata->msg_info.msg->body->data
                */
                if((!PjApiUa::getInstance()->getUaAccountHandler())||(!PjApiUa::getInstance()->getUaAccountHandler()->getAccountCallback()))
                {
                    PJ_LOG(4, ("hander or callback is not set",""));
                    return;
                }
                PjApiUa::getInstance()->getUaAccountHandler()->getAccountCallback()->onInfoRequest(call_id,
                            (std::string)(rdata->msg_info.msg->hdr.name.ptr),
                            (std::string)(rdata->msg_info.msg->body->content_type.type.ptr),
                            (unsigned long long)rdata->msg_info.msg->body->len,
                            rdata->msg_info.msg->body->data);

                status = pjsip_endpt_create_response(tsx->endpt, rdata,
                                     200, NULL, &tdata);
                if (status == PJ_SUCCESS)
                    status = pjsip_tsx_send_msg(tsx, tdata);

            }
            else
            {
                status = pjsip_endpt_create_response(tsx->endpt, rdata,
                                     400, NULL, &tdata);
                if (status == PJ_SUCCESS)
                    status = pjsip_tsx_send_msg(tsx, tdata);
            }
        }
    }
}

void PjApiUa::onTyping(pjsua_call_id call_id, const pj_str_t *from, const pj_str_t *to, const pj_str_t *contact, pj_bool_t is_typing)
{
    if((!PjApiUa::getInstance()->getUaAccountHandler())||(!PjApiUa::getInstance()->getUaAccountHandler()->getAccountCallback()))
    {
        PJ_LOG(4, ("ua","handler or callback is not set"));
        return;
    }

    bool isTyping;
    switch(is_typing)
    {
        case PJ_TRUE:
            isTyping = true;
            break;
        case PJ_FALSE:
            isTyping = false;
            break;
        default: break;
    }

    PjApiUa::getInstance()->getUaAccountHandler()->getAccountCallback()->onIMTyping(call_id,
                                                (std::string)((char*)from->ptr),
                                                (std::string)((char*)to->ptr),
                                                (std::string)((char*)contact->ptr),
                                                isTyping);
}

void PjApiUa::onIncomingCall(pjsua_acc_id acc_id, pjsua_call_id call_id,
			     pjsip_rx_data *rdata)
{
    PJ_LOG(3, ("UA","in coming call"));
    PJ_LOG(3,("ua", "Call %d: incoming call:\n%.*s",
          call_id,
          (int)rdata->msg_info.msg->body->len,
          rdata->msg_info.msg->body->data));

    if((!PjApiUa::getInstance()->getUaAccountHandler())||(!PjApiUa::getInstance()->getUaAccountHandler()->getAccountCallback()))
    {
        PJ_LOG(4, ("ua","handler or callback is not set"));
        return;
    }
    PjApiUa::getInstance()->getUaAccountHandler()->getAccountCallback()->onIncomingCall(acc_id,
                                            call_id,
                                            rdata->msg_info.msg->body->data);

}

void PjApiUa::setAutoAnswer(bool auto_answer)
{
    m_autoAnswer = auto_answer;
}

void PjApiUa::setCallback()
{
    PJ_LOG(3, ("UA", "set call back"));
    PjApiUa::m_uaConfig.cb.on_incoming_call = &(PjApiUa::onIncomingCall);
    PjApiUa::m_uaConfig.cb.on_call_state = &(PjApiUa::onCallState);
    PjApiUa::m_uaConfig.cb.on_pager = &(PjApiUa::onPager);
}

void PjApiUa::onPager(pjsua_call_id call_id,
                    const pj_str_t *from,
                    const pj_str_t *to,
                    const pj_str_t *contact,
                    const pj_str_t *mime_type,
                    const pj_str_t *body)
{
    PJ_LOG(3, ("On Pager","message received!"));
    printf("from: %s\n to: %s \n body: %s, contact: %s\n", from->ptr, to->ptr, body->ptr, body->ptr);
    //PjApiUa::getInstance()->getUaAccountHandler()->onIMMessage()

    if((!PjApiUa::getInstance()->getUaAccountHandler())||(!PjApiUa::getInstance()->getUaAccountHandler()->getAccountCallback()))
    {
        PJ_LOG(4, ("ua","handler or callback is not set"));
        return;
    }
    PJ_LOG(3, ("On Pager","message received2!"));

    PjApiUa::getInstance()->getUaAccountHandler()->getAccountCallback()->onIMMessage(call_id,
                                        (std::string)(from->ptr),
                                        (std::string)(to->ptr),
                                        (contact->slen==0)?(std::string)(contact->ptr):(std::string)((char*)to->ptr),
                                        mime_type->slen?(std::string)(mime_type->ptr):"",
                                        body->slen?(std::string)(body->ptr):"");


}


pj_bool_t PjApiUa::onStatelessRequest(pjsip_rx_data *rdata)
{
    PJ_LOG(3, ("PjApiUa","Stateless Request received!"));

    const pjsip_method info_method =
    {
        PJSIP_OTHER_METHOD,
        { "INFO2", 99 }
    };
    //pj_str_t info = "INFO2";
    if(strcmp(rdata->msg_info.msg->line.req.method.name.ptr,"INFO2") == 0)
    {
        PJ_LOG(3,("ua", " incoming INFO:\n %s \n ",
          rdata->msg_info.info));
    }
    if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &info_method)==0)
    {
       if ((rdata->msg_info.msg->body) && (rdata->msg_info.msg->body->len != 0) )
       {

            PJ_LOG(3,("ua", " incoming INFO:\n%.*s",
                  (int)rdata->msg_info.msg->body->len,
                  rdata->msg_info.msg->body->data));

             //Notify the upper layer, the incoming request
            /**
             * Define Info request format
             *  request name: -> rdata->info  pjsip_rx_data_get_info(rdata)
             *  request content type ->data->msg_info.msg->body->content_type.type + "/" + subtype
             *  request body: -> rdata->msg_info.msg->body->data
            */
            if((!PjApiUa::getInstance()->getUaAccountHandler())||(!PjApiUa::getInstance()->getUaAccountHandler()->getAccountCallback()))
            {
                PJ_LOG(4, ("hander or callback is not set",""));
                return PJ_FALSE;
            }
            //Todo
            /**  */
            std::string from = "siptest2";

            std::string content_t = (std::string)(rdata->msg_info.msg->body->content_type.type.ptr) +
                                    "/" +
                                    (std::string)(rdata->msg_info.msg->body->content_type.subtype.ptr);
            /**====================
             * Need to investigate if this alloc memory need to destroy manully */
            void* data = pj_pool_alloc(rdata->tp_info.pool, rdata->msg_info.msg->body->len);
            pj_memcpy(data, rdata->msg_info.msg->body->data, rdata->msg_info.msg->body->len);

            PjApiUa::getInstance()->getUaAccountHandler()->getAccountCallback()->onStatlessRequestReceived(from,
                        (std::string)(pjsip_rx_data_get_info(rdata)),
                        content_t,
                        (unsigned long long)rdata->msg_info.msg->body->len,
                        data);

        }
    }
}
